package info.niwota.ziplock.zip;

import info.niwota.ziplock.Debug;
import info.niwota.ziplock.FileItem;
import info.niwota.ziplock.StatusMonitor;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;

import android.util.Log;

/**
 * @author qiangli
 * 
 */
public class ZipUtils {

	private static final int BUFSIZ = 1024;
	private static final String TAG = "ZipUtils";

	public static void list(File top, File file, String ext, StatusMonitor status, List<FileItem> list)
			throws Exception {
		ZipFile zip = new ZipFile(file);
		for (Enumeration<?> e = zip.entries(); e.hasMoreElements();) {
			ZipEntry ze = (ZipEntry) e.nextElement();
			if (ze.isDirectory()) {
				continue;
			}
			String name = ze.getName();
			if (status != null) {
				status.send(name);
			}
			FileItem fi = new FileItem(ze.getName());
			list.add(fi);
		}
	}

	public static void unzip(File top, File zip, String ext, StatusMonitor status, List<FileItem> list)
			throws Exception {
		try {
			FileInputStream fis = new FileInputStream(zip);
			ZipInputStream zis = new ZipInputStream(fis);
			ZipEntry ze = zis.getNextEntry();
			while (ze != null) {
				if (!ze.isDirectory()) {
					String name = ze.getName();
					if (Debug.DEBUG) {
						Log.d("TAG", " unzip: " + name);
					}
					if (!name.equals("MANIFEST.MF")) {
						if (status != null) {
							status.send(name);
						}
						File f = new File(top, name + ext);
						File dir = f.getParentFile();
						if (dir != null && !dir.exists()) {
							dir.mkdirs();
						}
						FileOutputStream fos = new FileOutputStream(f);
						int n = 0;
						final byte[] buf = new byte[1024];
						while ((n = zis.read(buf, 0, 1024)) > -1) {
							fos.write(buf, 0, n);
						}
						fos.close();
						long modified = ze.getTime();
						f.setLastModified(modified);
						//
						FileItem fi = new FileItem(name);
						fi.setModified(modified);
						//
						list.add(fi);
					}
				}
				zis.closeEntry();
				ze = zis.getNextEntry();
			}
			zis.close();
		} catch (Exception e) {
			// e.printStackTrace();
			throw e;
		}
	}

	public static void extractFile(File zipfile, File dest, String name)
			throws IOException {
		FileInputStream fis = new FileInputStream(zipfile);
		ZipInputStream zis = new ZipInputStream(fis);
		try {
			extractFile(zis, dest, name);
		} finally {
			zis.close();
			fis.close();
		}
	}

	public static void extractFile(ZipInputStream zin, File dest, String name)
			throws IOException {
		ZipEntry ze = null;
		while ((ze = zin.getNextEntry()) != null) {
			if (!ze.isDirectory()) {
				String n = ze.getName();
				boolean same = sameEntry(n, name); // n.equals(name)
//				if (Debug.DEBUG) {
//					Log.d(TAG, "entry; " + n + " name: " + name + " same: "
//							+ same);
//				}
				if (same) {
					if (Debug.DEBUG) {
						Log.d(TAG, "entry found: " + n + " name: " + name);
					}
					// found
					File dir = dest.getParentFile();
					if (dir != null && !dir.exists()) {
						dir.mkdirs();
					}
					byte[] data = new byte[BUFSIZ];
					FileOutputStream out = new FileOutputStream(dest);
					try {
						for (int read; (read = zin.read(data, 0, BUFSIZ)) != -1;) {
							out.write(data, 0, read);
						}
					} finally {
						out.close();
					}
					dest.setLastModified(ze.getTime());
					zin.closeEntry();
					return;
				}
			}
			zin.closeEntry();
		}
	}

	public static ZipInputStream unzip(File zipfile, String name)
			throws IOException {
		FileInputStream fis = new FileInputStream(zipfile);
		ZipInputStream zis = new ZipInputStream(fis);
		ZipEntry ze = null;
		while ((ze = zis.getNextEntry()) != null) {
			if (!ze.isDirectory()) {
				String n = ze.getName();
				boolean same = sameEntry(n, name); // n.equals(name)
				if (Debug.DEBUG) {
					Log.d(TAG, "entry; " + n + " name: " + name + " same: "
							+ same);
				}
				if (same) {
					if (Debug.DEBUG) {
						Log.d(TAG, "entry; " + n + " name: " + name);
					}
					// found
					return zis;
				}
			}
			zis.closeEntry();
		}
		return null;
	}

	public static boolean sameEntry(String n1, String n2) {
		// compare name disregarding leading slash
		int idx1 = (n1.charAt(0) == '/' ? 1 : 0);
		int idx2 = (n2.charAt(0) == '/' ? 1 : 0);
		int len1 = n1.length() - idx1;
		int len2 = n2.length() - idx2;
		if (len1 != len2) {
			return false;
		}
		for (int i = 0; i < len1; i++) {
			char c1 = n1.charAt(idx1++);
			char c2 = n2.charAt(idx2++);
			if (c1 != c2) {
				return false;
			}

		}
		return true;
	}

	/**
	 * 
	 */
	private ZipUtils() {
	}
}
